/**
 * @fileoverview
 * This script defines a widget for fetching and displaying images from www.fly-ted.org.
 * @author <a href="http://purl.org/net/aliman">Alistair Miles</a>, Graham Klyne, Jun Zhao
 * @version $Revision:538 $ on $Date: 2008-08-27 09:08:41 +0100 (Wed, 27 Aug 2008) $ by $Author: aliman $
 * @requires YAHOO.util.Connect
 * @requires flyui
 * @requires flyui.flyted.Service
 * @requires flyui.flybase.Gene
 * TODO license terms
 */
 
// create a namespace if not already defined
flyui.namespace("flyui.flyted");


/*
 * --------------------------------------------------
 * WIDGET
 * --------------------------------------------------
 */


/**
 * Create a flyted image widget
 * @class
 * A widget for displaying images from www.fly-ted.org
 * @constructor
 * @param {flyui.flyted.Service} service the service to use to fetch data
 * @param {flyui.flyted.DefaultRenderer} renderer the renderer to use
 */
flyui.flyted.ImageWidget = function( service, renderer ) {

	var that = this;
	
	/** @private */
	this._controller = null;
	
	/** @private */
	this._renderer = renderer;
	
	/**
	 * @private
	 * Do initialisation of internal MVC
	 */
	this._init = function() {
		// create a model
		var model = new flyui.mvcutils.GenericModel2();
		model.setDefinition(flyui.flyted.ImageWidget.modelDefinition);
		
		// instantiate the controller
		this._controller = new flyui.flyted.ImageWidget.Controller(model, service, this);
		
		// connect the renderer to the model
		renderer.connect(model);
	};
	
	// do initialisation
	this._init();
	
};


/**
 * Find images by gene names used within fly-ted
 * @param {String} flytedGeneName a flyted gene name, see http://www.fly-ted.org/view/geneid/
 */
flyui.flyted.ImageWidget.prototype.findImagesByFlytedGeneName = function( flytedGeneName ) {
	// pass through to controller
	this._controller.findImagesByFlytedGeneName(flytedGeneName);
};


/**
 * Find images by flybase gene ID, using gene name translation data
 * @param {String} flybaseGeneID a flybase gene ID, e.g. FBgn123456
 */
flyui.flyted.ImageWidget.prototype.findImagesByFlybaseGeneID = function( flybaseGeneID ) {
	// pass through to controller
	this._controller.findImagesByFlybaseGeneID(flybaseGeneID);
};


/**
 * Find images by a gene object, translating gene names as needed.
 * @param {flyui.flybase.Gene} gene
 */
flyui.flyted.ImageWidget.prototype.findImagesByGene = function( gene ) {
	// pass through to controller
	this._controller.findImagesByGene(gene);
}


/*
 * --------------------------------------------------
 * CONTROLLER
 * --------------------------------------------------
 */


/**
 * Create a controller for a flyted image widget.
 * @class
 * A controller class for the flyted image widget internal MVC.
 * @constructor
 * @param {flyui.mvcutils.GenericModel2} model the model to store widget state data
 * @param {flyui.flyted.Service} service the service to use to fetch data
 * @param {flyui.flyted.ImageWidget} widget the widget to control
 */
flyui.flyted.ImageWidget.Controller = function( model, service, widget ) {
	
	var that = this;
	
	/**
	 * @private
	 */
	this._model = model;
	
	/**
	 * @private
	 */
	this._service = service;
	
	/**
	 * @private
	 */
	this._parent = widget;
	
	
	/**
	 * @private
	 * Success case callback.
	 * @param {Array<flyui.flyted.Image>} images
	 */	
	this._findImagesSuccess = function( images ) {
		log("request success");
		
		// set the results
		that._model.set("RESULTS", images);
		
		// set the state
		that._model.set("STATE", "READY");

	};
	
	
	/**
	 * @private
	 * Failure case callback.
	 * @param {Object} response the HTTP response (YUI)
	 */	
	this._findImagesFailure = function( response ) {

		log("request failed, status "+response.status+" "+response.statusText, "error");
		
		// set an error message
		var msg = "There was an error retrieving data from FlyTED, see the logs for more info. The server may be busy or down, please try again later. If this message persists, please contact the Image Bioinformatics Research Group at bioimage@mail.ontonet.org.";		

		that._model.set("ERRORMESSAGE", msg);

		// set the state
		that._model.set("STATE", "SERVERERROR");		
	};
	
};


/**
 * Find images by flyted gene name
 * @param {String} flytedGeneName a flyted gene name, see http://www.fly-ted.org/view/geneid/
 */
flyui.flyted.ImageWidget.Controller.prototype.findImagesByFlytedGeneName = function( flytedGeneName ) {
	// pass through to private implementation
	this._findImagesByFlytedGeneName(flytedGeneName, this._findImagesSuccess, this._findImagesFailure);
};


/**
 * @private
 * Implement find images by flyted gene name, allowing callback injection for testing
 * @param {String} flytedGeneName a flyted gene name, see http://www.fly-ted.org/view/geneid/
 * @param {Function} success success case callback
 * @param {Function} failure failure case callback
 */
flyui.flyted.ImageWidget.Controller.prototype._findImagesByFlytedGeneName = function( flytedGeneName, success, failure ) {

	log("flyui.flyted.ImageWidget.Controller._findImagesByFlytedGeneName :: request: "+flytedGeneName);
	
	// set the model pending
	this._model.set("STATE", "PENDING");
	
	// set the query property
	this._model.set("QUERY", flytedGeneName);
	
	// kick off the request
	this._service.findImagesByFlytedProbeName(flytedGeneName, success, failure);
};


/**
 * Find images by flybase gene ID
 * @param {String} flybaseGeneID a flybase gene ID, e.g. FBgn123456
 */
flyui.flyted.ImageWidget.Controller.prototype.findImagesByFlybaseGeneID = function( flybaseGeneID ) {
	// pass through to private implementation
	this._findImagesByFlybaseGeneID(flybaseGeneID, this._findImagesSuccess, this._findImagesFailure);
};


/**
 * @private
 * Implement find images by flybase gene ID, with callback injection for testing.
 * @param {String} flybaseGeneID a flybase gene ID, e.g. FBgn123456
 * @param {Function} success success case callback
 * @param {Function} failure failure case callback
 */
flyui.flyted.ImageWidget.Controller.prototype._findImagesByFlybaseGeneID = function( flybaseGeneID, success, failure ) {
	log("flyui.flyted.ImageWidget.Controller._findImagesByFlybaseGeneID :: request: "+flybaseGeneID);
	
	// set the model pending
	this._model.set("STATE", "PENDING");
	
	// set the query property
	this._model.set("QUERY", flybaseGeneID);
	
	// kick off the request
	this._service.findImagesByFlybaseGeneID(flybaseGeneID, success, failure);
};


/**
 * Find images by flybase gene object
 * @param {flyui.flybase.Gene} gene 
 */
flyui.flyted.ImageWidget.Controller.prototype.findImagesByGene = function( gene ) {
	// pass through to private implementation
	this._findImagesByGene(gene, this._findImagesSuccess, this._findImagesFailure);
};


/**
 * @private
 * Implement find images by flybase gene object, with callback injection to support testing
 * @param {flyui.flybase.Gene} gene 
 * @param {Function} success success case callback
 * @param {Function} failure failure case callback
 */
flyui.flyted.ImageWidget.Controller.prototype._findImagesByGene = function( gene, success, failure ) {
	log("flyui.flyted.ImageWidget.Controller._findImagesByGene :: request: "+gene.flybaseID);
	
	// set the model pending
	this._model.set("STATE", "PENDING");
	
	// set the query property
	this._model.set("QUERY", gene);
	
	// kick off the request
	this._service.findImagesByFlybaseGeneID(gene.flybaseID, success, failure);
};


/*
 * --------------------------------------------------
 * MODEL DEFINITION
 * --------------------------------------------------
 */


/**
 * Definition of flyted imagewidget model.
 */
flyui.flyted.ImageWidget.modelDefinition = {

	properties : [ "STATE", "RESULTS", "QUERY", "ERRORMESSAGE" ],
	
	values : {
		"STATE" : [ "PENDING", "READY", "SERVERERROR", "UNEXPECTEDERROR" ]
	},
	
	initialize : function( data ) {
		data["STATE"] = "READY";
		data["RESULTS"] = null;
		data["QUERY"] = null;
		data["ERRORMESSAGE"] = null;
	}

};


/*
 * --------------------------------------------------
 * DEFAULT RENDERER
 * --------------------------------------------------
 */


/**
 * TODO doc me
 */
flyui.flyted.ImageWidget.DefaultRenderer = function() {};


/**
 * TODO doc me
 */
flyui.flyted.ImageWidget.DefaultRenderer.prototype.setCanvas = function( canvas ) {
    this._canvas = canvas;
    this._initCanvas();
};


/**
 * @private
 * TODO doc me
 */
flyui.flyted.ImageWidget.DefaultRenderer.prototype._initCanvas = function() {

    // set up the pending pane
    this._pendingPane = document.createElement("div");
    this._pendingPane.innerHTML = "<p>pending...</p>";
    this._canvas.appendChild(this._pendingPane);
    YAHOO.util.Dom.addClass(this._pendingPane, "pendingPane");
    flyui.mvcutils.hide(this._pendingPane);
    
    // set up the message pane
    this._messagePane = document.createElement("div");
    this._messagePane.innerHTML = "this should never be displayed";
    this._canvas.appendChild(this._messagePane);
    YAHOO.util.Dom.addClass(this._messagePane, "messagePane");
    flyui.mvcutils.hide(this._messagePane);
    
    // setup results summary pane
    this._resultsSummaryPane = document.createElement("p");
    this._canvas.appendChild(this._resultsSummaryPane);
    YAHOO.util.Dom.addClass(this._resultsSummaryPane, "resultsSummaryPane");
    flyui.mvcutils.hide(this._resultsSummaryPane);
    
    // setup results pane
    this._resultsPane = document.createElement("div");
    this._canvas.appendChild(this._resultsPane);
    YAHOO.util.Dom.addClass(this._resultsPane, "resultsPane");
    flyui.mvcutils.hide(this._resultsPane);
};

/**
 * @private
 */
flyui.flyted.ImageWidget.DefaultRenderer.prototype._canvas = null;

/**
 * @private
 */
flyui.flyted.ImageWidget.DefaultRenderer.prototype._pendingPane = null;

/**
 * @private
 */
flyui.flyted.ImageWidget.DefaultRenderer.prototype._resultsPane = null;

/**
 * @private
 */
flyui.flyted.ImageWidget.DefaultRenderer.prototype._resultsSummaryPane = null;

/**
 * @private
 */
flyui.flyted.ImageWidget.DefaultRenderer.prototype._messagePane = null;

/**
 * @private
 * Main callback function for model changes.
 * @param {String} type the name of the model property changed
 * @param {Array} args the callback args
 * @param {flyui.genefinder.DefaultRenderer} self a self reference, to work around callback issues
 */
flyui.flyted.ImageWidget.DefaultRenderer.prototype._onModelChanged = function(type, args, self) {
    var handlers = {
        "STATE":"_onStateChanged",
        "QUERY":"_onQueryChanged",
        "RESULTS":"_onResultsChanged",
        "ERRORMESSAGE":"_onErrorMessageChanged",
    }
    var handler = handlers[type];
    // call the handler
    self[handler](args[0], args[1]);
}


/**
 * @private
 * TODO doc me
 */
flyui.flyted.ImageWidget.DefaultRenderer.prototype._onStateChanged = function( from, to ) {
    if ( to == "PENDING" ) {
	    flyui.mvcutils.show(this._pendingPane);
	    flyui.mvcutils.hide(this._messagePane);
	    flyui.mvcutils.hide(this._resultsSummaryPane);
	    flyui.mvcutils.hide(this._resultsPane);
	} 
	else if (to == "READY") {
	    flyui.mvcutils.hide(this._pendingPane);
	    flyui.mvcutils.hide(this._messagePane);
	    flyui.mvcutils.show(this._resultsSummaryPane);
	    flyui.mvcutils.show(this._resultsPane);     
	}
	else if ( to == "SERVERERROR" || to == "UNEXPECTEDERROR" ) {
	    flyui.mvcutils.hide(this._pendingPane);
	    flyui.mvcutils.show(this._messagePane);
	    flyui.mvcutils.hide(this._resultsSummaryPane);
	    flyui.mvcutils.hide(this._resultsPane);         
	} 
	else {
	    // this should never happen
    	throw {name:"flyui.flyted.ImageWidget.UnexpectedStateError", message:"Invalid state: "+newState};
	}
};


/**
 * @private
 * TODO doc me
 */
flyui.flyted.ImageWidget.DefaultRenderer.prototype._onQueryChanged = function( from, to ) {

    // store query
    this._query = to;
};


/**
 * @private
 * TODO doc me
 */
flyui.flyted.ImageWidget.DefaultRenderer.prototype._onResultsChanged = function( from, to ) {
	var probes = null;
	if (to.length > 0) {
		probes = new Array();
		for (var i in to){
			var labels = to[i].probe.probeLabels;
			for (var l in labels){
				flyui.util.appendIfNotMember(probes, labels[l]);
			}
		}
	}
	
    // render the results summary
    log("render the results summary", "debug");
    this._renderResultsSummary(this._query, to.length, probes);
    
    if (to.length > 0) {
        log("render the results "+to.length, "debug");
        
        // set the content
        this._resultsPane.innerHTML = this._imagesToDivHTML(to); 

    }
    else {
        log("empty results, empty results pane");
        this._resultsPane.innerHTML = "";
    }

};
  
  
/**
 * @private
 */
flyui.flyted.ImageWidget.DefaultRenderer.prototype._onErrorMessageChanged = function( from, to) {
    this._messagePane.innerHTML = to;
};


/**
 * TODO doc me
 */
flyui.flyted.ImageWidget.DefaultRenderer.prototype.connect = function( model ) {

    model.subscribeAll(this._onModelChanged, this);

};


/**
 * @private
 * TODO doc me
 */
flyui.flyted.ImageWidget.DefaultRenderer.prototype._renderResultsSummary = function( query, count, probeLabels ) {
	log("building results summary content", "debug");
    var content = "showing <span>";
    content += count;
    content += "</span> matching image";
    content += (count == 0 || count > 1) ? "s " : " ";
    content += "from <a href='http://www.fly-ted.org'>www.fly-ted.org</a> for ";
        
    if (query instanceof flyui.flybase.Gene) {
    	log("a flybaseid in the query: " +query);
        content += "gene <strong>"+query.symbols.join(" / ")+"</strong>";
        
        //var ftnames = flyui.flyted.GeneNameMappings.flybaseToFlyted[query.flybaseID];
        
        var ftnames = probeLabels;
        
        log ("the flyted probe names " + ftnames.length);
        
        if (typeof ftnames == "undefined" || !ftnames) {
            content += " (no mapping found to FlyTED gene names";
        }
        else if (ftnames.length == 1) {
            content += " (mapped to FlyTED gene name <strong><a href='http://www.fly-ted.org/view/geneid/"+ftnames[0]+".html'>"+ftnames[0]+"</a></strong>";         
        }
        else if (ftnames.length > 1) {
            content += "(mapped to FlyTED gene names ";
            for (var i=0; i<ftnames.length; i++) {
                content += "<strong><a href='http://www.fly-ted.org/view/geneid/"+ftnames[i]+".html'>"+ftnames[i]+"</a></strong>";
                if (i < ftnames.length-1) content += ", ";
            }
        }       
        content += ", N.B. gene name mappings may contain errors, check source data carefully)";
    } else {
    	log("not a flybaseid in the query :"+ query);
        content +=  "query <span>'"+query+"'</span>";
    }
    this._resultsSummaryPane.innerHTML = content;
}

/**
 * @private
 * TODO doc me
 */
flyui.flyted.ImageWidget.DefaultRenderer.prototype._imagesToDivHTML = function( images ) {

    // build the divs
    
    log("build div content for images "+images.length, "debug");
    var content = "";
    for ( var i in images ) {
        
        content += this._imageToDivHTML(images[i]);
        log ("Generate the img div" + content, "debug"); 
    }
    
    return content;
};


/**
 * @private
 * TODO doc me
 */
flyui.flyted.ImageWidget.DefaultRenderer.prototype._imageToDivHTML = function( image ) {
  
    log("build content for image "+image.caption, "debug");
    
    var content =   "<div class=\"result\">";
    content +=          "<p><a href=\"" + image.flytedURL + "\">";
    content +=              "<img src=\"" + image.thumbnailURL + "\" alt=\"this is an image\">";
    content +=          "</img></a></p>"; 
    content +=          "<p class=\"caption\">"+image.caption+"</p>"
    content +=      "</div>";
    
    return content;
};

